package com.ruoyi.violate.service.impl;

import java.awt.*;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.csv.CsvUtil;
import cn.hutool.core.text.csv.CsvWriter;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.CharsetUtil;

import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;

import com.alibaba.excel.EasyExcel;
import com.alibaba.fastjson2.JSON;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.file.FileUploadUtils;
import com.ruoyi.common.utils.uuid.IdUtils;
import com.ruoyi.framework.web.domain.server.Sys;
import com.ruoyi.violate.domain.ViolateTilte;
import com.ruoyi.violate.domain.ViolateWords;
import com.ruoyi.violate.mapper.ViolateWordsMapper;
import com.ruoyi.violate.service.IViolateTilteService;

import com.ruoyi.web.controller.common.PageReadListener;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.fileupload.disk.DiskFileItem;
import org.apache.commons.io.FileUtils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import com.ruoyi.violate.mapper.ViolateFileMapper;
import com.ruoyi.violate.domain.ViolateFile;
import com.ruoyi.violate.service.IViolateFileService;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.commons.CommonsMultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 文件上传Service业务层处理
 *
 * @author 谭拓宇
 * @date 2022-12-03
 */
@Service
@Slf4j
public class ViolateFileServiceImpl implements IViolateFileService {
    //private static ExecutorService executor = ThreadUtil.newExecutor(5);
    @Autowired
    private ViolateFileMapper violateFileMapper;
    @Autowired
    private ViolateWordsMapper violateWordsMapper;
    @Autowired
    private IViolateTilteService violateTilteService;
    @Autowired
    private RedisCache redisCache;


    /**
     * 查询文件上传
     *
     * @param id 文件上传主键
     * @return 文件上传
     */
    @Override
    public ViolateFile selectViolateFileById(String id) {
        return violateFileMapper.selectViolateFileById(id);
    }

    /**
     * 查询文件上传列表
     *
     * @param violateFile 文件上传
     * @return 文件上传
     */
    @Override
    public List<ViolateFile> selectViolateFileList(ViolateFile violateFile) {
        return violateFileMapper.selectViolateFileList(violateFile);
    }

    /**
     * 新增文件上传
     *
     * @param violateFile 文件上传
     * @return 结果
     */
    @Override
    public int insertViolateFile(ViolateFile violateFile) {
        violateFile.setId(IdUtils.simpleUUID());
        violateFile.setDownloadFilePath("");
        violateFile.setFileName(violateFile.getFilePath().substring(violateFile.getFilePath().lastIndexOf("/") + 1, violateFile.getFilePath().length()));
        violateFile.setCreateTime(DateUtils.getNowDate());
        violateFile.setCreateBy(SecurityUtils.getUserId() + "");
        System.out.println("violateFile的id:" + violateFile.getId());
        //往mysql中添加file对象
        int i = violateFileMapper.insertViolateFile(violateFile);
        //多线程  处理文件
        ThreadUtil.execAsync(() -> {
            try {
//                fileViolatePathById(violateFile.getId());
                fileViolatePathByIdNosql(violateFile.getId());
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
        return i;
    }

    private void fileViolatePathByIdNosqlNew(String id) throws Exception {
        //通过id获取 文件
        ViolateFile violateFile = this.selectViolateFileById(id);

        //获取所有的违禁词
        List<ViolateWords> words = violateWordsMapper.selectViolateWordsListByLength();
        String filePath = RuoYiConfig.getProfile() + violateFile.getFilePath().substring(8, violateFile.getFilePath().length());
        System.out.println(filePath);

        HashMap<String, String> wordsMap = new HashMap<>();
        for (ViolateWords word : words) {
            wordsMap.put(word.getWords() , word.getReplaces()) ;
        }

        //获取表格所有数据list
        List<ViolateTilte> list = new ArrayList<ViolateTilte>();
        EasyExcel.read(filePath, ViolateTilte.class, new PageReadListener<ViolateTilte>(dataList -> {
            for (ViolateTilte violateTilte : dataList) {
                log.info("读取到一条数据{}", JSON.toJSONString(violateTilte));
                if (violateTilte.getTilte() != null){
                    list.add(violateTilte) ;
                }

            }
        })).sheet().doRead();

        // 过滤进度条
        String filter = "/" + list.size();
        violateFile.setFilter(list.size() +filter) ;
        int num = 1 ;

        //过滤违禁词核心代码
        for (int i = 0, len = list.size(); i < len; i++) {
            //如果title中有map中的关键字，在处理

            redisCache.setCacheObject("file_filter:" + id, i + filter);
        }
    }


    //过滤违禁词，但是不保存数据库，保存为Excel
    private void fileViolatePathByIdNosql(String id) throws Exception {

        // newFixedThreadPool是固定线程池，10代表线程池数量
        ExecutorService pool = Executors.newFixedThreadPool(5);
//        ExecutorService executor = ThreadUtil.newExecutor(5);
        //通过id获取 文件
        ViolateFile violateFile = this.selectViolateFileById(id);

        //获取所有的违禁词
        List<ViolateWords> words = violateWordsMapper.selectViolateWordsListByLength();
        String filePath = RuoYiConfig.getProfile() + violateFile.getFilePath().substring(8, violateFile.getFilePath().length());
        System.out.println(filePath);

//        File file = new File(filePath);
//        MultipartFile files = getMultipartFile(file);
//        ExcelReader reader = ExcelUtil.getReader(files.getInputStream());
//        reader.addHeaderAlias("标题", "tilte");
//        reader.addHeaderAlias("链接", "url");
//        reader.addHeaderAlias("价格", "money");
//        reader.addHeaderAlias("类目", "type");
//        reader.addHeaderAlias("利润标签", "states");
//        //获取所有的Excel表格中的tilte
//        List<ViolateTilte> list = reader.readAll(ViolateTilte.class);

        List<ViolateTilte> list = new ArrayList<ViolateTilte>();
        List<ViolateTilte> removeList = new ArrayList<ViolateTilte>();
        EasyExcel.read(filePath, ViolateTilte.class, new PageReadListener<ViolateTilte>(dataList -> {
            for (ViolateTilte violateTilte : dataList) {
                log.info("读取到一条数据{}", JSON.toJSONString(violateTilte));
                if (violateTilte.getTilte() != null){
                    list.add(violateTilte) ;
                }

            }
        })).sheet().doRead();

        // 过滤进度条
        String filter = "/" + list.size();
        violateFile.setFilter(list.size() +filter) ;
        int num = 1 ;

        //将tilte和违禁词做对比，如果出现
        //过滤数据，循环遍历Excel表中的数据与违禁词一一对比，效率低下
        for (int i = 0, len = list.size(); i < len; i++) {
//            redisCache.setCacheObject("file_filter:" + id,num + filter);
            num++ ;
            int finalNum = i;
            // 创建Runnable对象
            Runnable runnable = new Runnable() {
            // 重写runnable对象中的run方法
            @Override
            public void run() {
                log.info("循环次数："+finalNum);
              // 业务逻辑 过滤关键词
              for (ViolateWords violateWords : words) {
                if (StringUtils.isNotEmpty(violateWords.getWords())) {
                    String tilte = list.get(finalNum).getTilte().replaceAll(" ", "");
                    if (tilte.indexOf(violateWords.getWords()) >= 0) {
                        String rep = "";
                        //如果替换词是删除，则直接将数据删掉
                        if ("删除".equals(violateWords.getReplaces())) {
                            removeList.add(list.get(finalNum)) ;
//                            list.remove(finalNum);
//                            i--;
//                            len--;
//                            break;
                            continue;
                        } else if (StringUtils.isNotEmpty(violateWords.getReplaces())) //如果替换词不是删除，则替换关键词
                            rep = violateWords.getReplaces();
                            tilte = tilte.replace(violateWords.getWords(), rep);
                            list.get(finalNum).setTilte(tilte);
                    }
                }
            }
                    redisCache.setCacheObject("file_filter:" + id,finalNum + filter);
                    log.info("执行{}条语句！！！",finalNum);
           }
          };
            // 步骤1 中创建的线程池对runnable对象调用
            pool.execute(runnable);


//            //executor.execute(() -> {
//            for (ViolateWords violateWords : words) {
//                if (StringUtils.isNotEmpty(violateWords.getWords())) {
//                    String tilte = list.get(i).getTilte().replaceAll(" ", "");
//                    if (tilte.indexOf(violateWords.getWords()) >= 0) {
//                        String rep = "";
//                        //如果替换词是删除，则直接将数据删掉
//                        if ("删除".equals(violateWords.getReplaces())) {
//                            list.remove(i);
//                            i--;
//                            len--;
//                            break;
//                        } else if (StringUtils.isNotEmpty(violateWords.getReplaces())) //如果替换词不是删除，则替换关键词
//                            rep = violateWords.getReplaces();
//                        tilte = tilte.replace(violateWords.getWords(), rep);
//                        list.get(i).setTilte(tilte);
//                    }
//                }
//            }
//            //});
        }
        // 步骤1 中线程池，进行关闭
        pool.shutdown();
        System.out.println("关闭线程！！！");
        //之后需要做一个判断，判断只有所有的线程都执行完才能往下执行
        while(true){
            if(pool.isTerminated()){
                //执行删除操作
                for (ViolateTilte violateTilte : removeList) {
                    list.remove(violateTilte) ;
                }
                log.info("线程关闭！！！！");
                break;
            }
        }

        redisCache.deleteObject("file_filter:" + id);
        //过滤后的数据，保存为excel表格
        Set<String> includeColumnFiledNames = new HashSet<String>();
        includeColumnFiledNames.add("url");
        includeColumnFiledNames.add("tilte");
        includeColumnFiledNames.add("money");
        includeColumnFiledNames.add("type");
        includeColumnFiledNames.add("type1");
        includeColumnFiledNames.add("states");
        EasyExcel.write(filePath, ViolateTilte.class).includeColumnFiledNames(includeColumnFiledNames).sheet("数据表").doWrite(list);
        log.info("过滤后生成新的表格成功！！！");
        violateFile.setDelFlag("1");
        updateViolateFile(violateFile);
    }


    private void fileViolatePathById(String id) throws Exception {
        //通过id获取 文件
        ViolateFile violateFile = this.selectViolateFileById(id);

        List<ViolateWords> words = violateWordsMapper.selectViolateWordsListByLength();
        String filePath = RuoYiConfig.getProfile() + violateFile.getFilePath().substring(8, violateFile.getFilePath().length());
        System.out.println(filePath);

        File file = new File(filePath);
        MultipartFile files = getMultipartFile(file);
        ExcelReader reader = ExcelUtil.getReader(files.getInputStream());
        reader.addHeaderAlias("标题", "tilte");
        reader.addHeaderAlias("链接", "url");
        reader.addHeaderAlias("价格", "money");
        reader.addHeaderAlias("类目", "type");
        List<ViolateTilte> list = reader.readAll(ViolateTilte.class);
//        int num = (int) Math.ceil((double) list.size() / 6000.0);
//        List<ViolateTilte> lsitAll = new ArrayList<>();
//        int num = (int) Math.ceil((double) list.size() / 10000.0);
//        System.out.println("num :" + num);
//        List<List<ViolateTilte>> listlsit = averageAssign(list, num);
//        for (int j = 0; j < listlsit.size(); j++) {
//            final int J = j;
//            executor.execute(new Runnable() {
//                @Override
//                public void run() {
//                    List<ViolateTilte> violateTilteList = listlsit.get(J);
//                    for (int i = 0, len = violateTilteList.size(); i < len; i++) {
//                        for (ViolateWords violateWords : words) {
//                            if (StringUtils.isNotEmpty(violateWords.getWords())) {
//                                String tilte = list.get(i).getTilte().replaceAll(" ", "");
//                                if (tilte.indexOf(violateWords.getWords()) >= 0) {
//                                    String rep = "";
//                                    if ("删除".equals(violateWords.getReplaces())) {
//                                        list.remove(i);
//                                        i--;
//                                        len--;
//                                        break;
//                                    } else if (StringUtils.isNotEmpty(violateWords.getReplaces()))
//                                        rep = violateWords.getReplaces();
//                                    tilte = tilte.replace(violateWords.getWords(), rep);
//                                    list.get(i).setTilte(tilte);
//                                }
//                            }
//                        }
//                        System.out.println(Thread.currentThread().getName() + ":" + list.get(i));
//                    }
//                    //循环添加
//                    //
//                    lsitAll.addAll(violateTilteList);
//                }
//            });
//        }
//        if (executor.isTerminated()) {
//            System.out.println(lsitAll.size());
//        }
        //过滤数据，循环遍历Excel表中的数据与违禁词一一对比，效率低下
        for (int i = 0, len = list.size(); i < len; i++) {
            //executor.execute(() -> {
            for (ViolateWords violateWords : words) {
                if (StringUtils.isNotEmpty(violateWords.getWords())) {
                    String tilte = list.get(i).getTilte().replaceAll(" ", "");
                    if (tilte.indexOf(violateWords.getWords()) >= 0) {
                        String rep = "";
                        //如果替换词是删除，则直接将数据删掉
                        if ("删除".equals(violateWords.getReplaces())) {
                            list.remove(i);
                            i--;
                            len--;
                            break;
                        } else if (StringUtils.isNotEmpty(violateWords.getReplaces())) //如果替换词不是删除，则替换关键词
                            rep = violateWords.getReplaces();
                        tilte = tilte.replace(violateWords.getWords(), rep);
                        list.get(i).setTilte(tilte);
                    }
                }
            }
            //});
            System.out.println(list.get(i));
        }
        ExecutorService executor = ThreadUtil.newExecutor(5);
        for (ViolateTilte entity : list) {
            executor.execute(() -> {
                entity.setFileId(id);
                //在数据库中violate_tilte表中添加tilte数据
                violateTilteService.insertViolateTilte(entity);
            });
        }
        ViolateTilte violateTilte = new ViolateTilte();
        violateTilte.setFileId(id);
        while (!executor.isTerminated()) {
            System.out.println("线程还在处理未结数");
            //在violate_tilte表中查询匹配的数据，fileid匹配
            Long count = violateTilteService.selectViolateTilteListByCount(violateTilte);
            //相等说明数据已经加载完成，修改violateFile的状态和时间
            if (count == list.size()) {
                violateFile.setDelFlag("1");
                updateViolateFile(violateFile);
                executor.shutdown();
            } else {
                TimeUnit.SECONDS.sleep(2);
            }
        }
        System.out.println("结束");
//        if(executor.isTerminated()){
//            violateFile.setDelFlag("1");
//            updateViolateFile(violateFile);
//        }
//        ThreadUtil.execAsync(() -> {
//            String fileName = RuoYiConfig.getProfile() + "/" + violateFile.getFileName();
//            File f = null;
//            try {
//                f = createCsvFile(list, fileName);
//            } catch (IOException e) {
//                e.printStackTrace();
//            }
//            String path = uploadFile(getMultipartFile(f));
//            f.delete();
//            violateFile.setDownloadFilePath(path);
//            violateFileMapper.updateViolateFile(violateFile);
//        });
    }

    public void export(List<List<String>> rowAll, String fileName) throws IOException {
        ExcelWriter writer = ExcelUtil.getWriter();
        writer.setColumnWidth(-1, 30);
        writer.setRowHeight(-1, 20);
        //你要保存到哪里 就给哪里的路径就行了
        FileOutputStream output = new FileOutputStream(fileName);
        // 一次性写出内容
        writer.write(rowAll);
        writer.flush(output);
        // 关闭writer，释放内存
        writer.close();
    }

    public File createCsvFile(List<ViolateTilte> list, String fileName) throws IOException {
        List<List<String>> rowAll = new ArrayList<List<String>>();
        List<String> row = CollUtil.newArrayList("链接", "标题", "价格", "类目");
        rowAll.add(row);
        for (int i = 0; i < list.size(); i++) {
            ViolateTilte entity = list.get(i);
            List<String> rowItem = CollUtil.newArrayList(entity.getUrl(), entity.getTilte(), entity.getMoney(), entity.getType());
            rowAll.add(rowItem);
        }
        export(rowAll, fileName);
        return new File(fileName);
    }

    /**
     * 将一个list均分成n个list,主要通过偏移量来实现的
     *
     * @param source 源集合
     * @param n      分成多少份
     * @return
     */
    public <T> List<List<T>> averageAssign(List<T> source, int n) {
        List<List<T>> result = new ArrayList<List<T>>();
        // 集合为空或 n <= 0 直接返回
        if (StringUtils.isEmpty(source) || n <= 0) {
            return result;
        }
        int remaider = source.size() % n;  // (先计算出余数)
        int number = source.size() / n;  // 然后是商
        int offset = 0; // 偏移量
        for (int i = 0; i < n; i++) {
            List<T> value = null;
            if (remaider > 0) {
                value = source.subList(i * number + offset, (i + 1) * number + offset + 1);
                remaider--;
                offset++;
            } else {
                value = source.subList(i * number + offset, (i + 1) * number + offset);
            }
            result.add(value);
        }
        return result;
    }

    public MultipartFile getMultipartFile(File file) {
        DiskFileItem item = new DiskFileItem("file"
                , MediaType.MULTIPART_FORM_DATA_VALUE
                , true
                , file.getName()
                , (int) file.length()
                , file.getParentFile());
        try {
            OutputStream os = item.getOutputStream();
            os.write(FileUtils.readFileToByteArray(file));
        } catch (IOException e) {
            e.printStackTrace();
        }

        return new CommonsMultipartFile(item);
    }

    /**
     * 通用上传请求
     */
    public String uploadFile(MultipartFile file) {
        try {
            // 上传文件路径
            String filePath = RuoYiConfig.getUploadPath();
            // 上传并返回新文件名称
            String fileName = FileUploadUtils.upload(filePath, file);

            return fileName;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 修改文件上传
     *
     * @param violateFile 文件上传
     * @return 结果
     */
    @Override
    public int updateViolateFile(ViolateFile violateFile) {
        violateFile.setUpdateTime(DateUtils.getNowDate());
        return violateFileMapper.updateViolateFile(violateFile);
    }

    /**
     * 批量删除文件上传
     *
     * @param ids 需要删除的文件上传主键
     * @return 结果
     */
    @Override
    public int deleteViolateFileByIds(String[] ids) {
        return violateFileMapper.deleteViolateFileByIds(ids);
    }

    /**
     * 删除文件上传信息
     *
     * @param id 文件上传主键
     * @return 结果
     */
    @Override
    public int deleteViolateFileById(String id) {
        return violateFileMapper.deleteViolateFileById(id);
    }

    /**
     * 下载
     *
     * @param id
     */
    @Override
    public void exportToId(String id, HttpServletRequest request, HttpServletResponse response) {
        //获取文件对象
        ViolateFile violateFile = selectViolateFileById(id);
        File excelFile = new File(violateFile.getFilePath());
//        //生成tiltte数据对象
//        ViolateTilte violateTilte = new ViolateTilte();
//        violateTilte.setFileId(violateFile.getId());
//        List<ViolateTilte> list = violateTilteService.selectViolateTilteList(violateTilte);


        // 下载本地文件
        // 文件的默认保存名
        String fileName = RuoYiConfig.getProfile() + violateFile.getFilePath().substring(8, violateFile.getFilePath().length());

        try {
            // path是指欲下载的文件的路径。
            File file = new File(fileName);
            // 取得文件名。
            String filename = file.getName();
            // 取得文件的后缀名。
            String ext = filename.substring(filename.lastIndexOf(".") + 1).toUpperCase();

            // 以流的形式下载文件。
            InputStream fis = new BufferedInputStream(new FileInputStream(fileName));
            byte[] buffer = new byte[fis.available()];
            fis.read(buffer);
            fis.close();
            // 清空response
            response.reset();
            // 设置response的Header
            response.addHeader("Content-Disposition", "attachment;filename=" + new String(filename.getBytes()));
            response.addHeader("Content-Length", "" + file.length());
            OutputStream toClient = new BufferedOutputStream(response.getOutputStream());
            response.setContentType("application/octet-stream");
            toClient.write(buffer);
            toClient.flush();
            toClient.close();
        } catch (IOException ex) {
            log.info("下载异常："+ex.getMessage());
            ex.printStackTrace();
        }

    }

    /**
     * 重新加载
     * @param id
     */
    public void cxjz(String id) {

        Long userId = SecurityUtils.getUserId();
        ViolateFile violateFile = new ViolateFile();
        violateFile.setId(id);
        violateFile.setDelFlag("0");
        this.violateFileMapper.updateViolateFile(violateFile);
        ThreadUtil.execAsync(() -> {
            try {
                this.fileViolatePathByIdNosql(id);
            } catch (Exception var4) {
                var4.printStackTrace();
            }

        });
    }
}
